﻿/*
--------------------------------------
File: GrowingSelection.cs
Project: Themaopdracht 7
Author: Roel Blaauwgeers
Adopted from Java source code:
http://www.labbookpages.co.uk/software/imgProc/blobDetection.html
Based upon the work of A.Greensted
Description:
Label the image and find blobs
--------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace THO7_Team8
{
    public class BlobDetector
    {
        private int pictureWidth;
        private int pictureHeight;

        Bitmap originalBW;
        Bitmap original;

        public int blobCount = 0;

        public int[] labeledImage;
        private int[] labelTable;

        //Constructor
        public BlobDetector(Bitmap originalBW, Bitmap original)
        {
            this.originalBW = originalBW;
            this.original = original;
            pictureWidth = original.Width;
            pictureHeight = original.Height;

            labeledImage = new int[pictureWidth * pictureHeight];

            int tableSize = pictureWidth * pictureHeight / 4;

            labelTable = new int[tableSize];
        }


        
        public void findBlobs()
        {
            //Mask:
            //  A   B   C
            //  D   X
            //
            int indexX = 0;                   //X
            int indexA = -pictureWidth - 1;   //A
            int indexB = -pictureWidth;       //B
            int indexC = -pictureWidth + 1;   //C
            int indexD = -1;                  //D

            int label = 1;  //First following unused label

            //Fast bitmap
            UnsafeBitmap ubmp = new UnsafeBitmap(originalBW);
            ubmp.LockBitmap();

            // Loop through all pixels looking for connected regions. Assigning labels
            for (int y = 0; y < pictureHeight; y++)
            {
                for (int x = 0; x < pictureWidth; x++)
                {
                    labeledImage[indexX] = 0;

                    // Check if on foreground pixel
                    if (ubmp.GetPixel(x, y).red < 128)
                    {
                        // Find label for neighbours (0 if out of range)
                        int aLabel = (x > 0 && y > 0) ? labelTable[labeledImage[indexA]] : 0;
                        int bLabel = (y > 0) ? labelTable[labeledImage[indexB]] : 0;
                        int cLabel = (x < pictureWidth - 1 && y > 0) ? labelTable[labeledImage[indexC]] : 0;
                        int dLabel = (x > 0) ? labelTable[labeledImage[indexD]] : 0;

                        // Look for label with least label value
                        int min = Int32.MaxValue;
                        if (aLabel != 0 && aLabel < min) min = aLabel;
                        if (bLabel != 0 && bLabel < min) min = bLabel;
                        if (cLabel != 0 && cLabel < min) min = cLabel;
                        if (dLabel != 0 && dLabel < min) min = dLabel;

                        // If no neighbours in foreground
                        if (min == Int32.MaxValue)
                        {
                            labeledImage[indexX] = label;
                            labelTable[label] = label;

                            label++;
                        }

                        // Neighbour found
                        else
                        {
                            // Label pixel with lowest label from neighbours
                            labeledImage[indexX] = min;

                            //Also label neigbours with lowest label if foreground
                            if (aLabel != 0) labelTable[aLabel] = min;
                            if (bLabel != 0) labelTable[bLabel] = min;
                            if (cLabel != 0) labelTable[cLabel] = min;
                            if (dLabel != 0) labelTable[dLabel] = min;
                        }
                    }

                    //Increment the pointers
                    indexX++;
                    indexA++;
                    indexB++;
                    indexC++;
                    indexD++;
                }
            }

            //Release read bitmap
            ubmp.UnlockBitmap();

            //For all assigned labels
            for (int i = label - 1; i > 0; i--)
            {
                //If label belongs to blob with other label
                if (labelTable[i] != i)
                {
                    int l = i;
                    while (l != labelTable[l]) l = labelTable[l];
                    labelTable[i] = l;
                }
            }

            // Renumber lables into sequential numbers, starting with 0
            int newLabel = 1;
            for (int i = 1; i < label; i++)
            {
                if (labelTable[i] == i) labelTable[i] = newLabel++;
                else labelTable[i] = labelTable[labelTable[i]];
            }

            //Blob count is the number of assigned labels
            blobCount = newLabel;

            //Replace labels corresponding the labelTable 
            for (int i = 0; i < labeledImage.Length; i++)
            {
                if (labeledImage[i] != 0)
                {
                    labeledImage[i] = labelTable[labeledImage[i]];
                }
            }
        }

        public Bitmap[] getLicensePlates(Point[][] candidates)
        {
            Bitmap[] candidates2 = new Bitmap[candidates.Length];

            for (int i = 0; i < candidates.Length; i++)
            {
                if (candidates[i] != null)
                {
                    int smallestX = pictureWidth;
                    int smallestY = pictureHeight;
                    int biggestX = 0;
                    int biggestY = 0;

                    for (int j = 0; j < 4; j++)
                    {
                        int curX = candidates[i][j].X;
                        int curY = candidates[i][j].Y;

                        if (curX < smallestX) smallestX = curX;
                        if (curX > biggestX) biggestX = curX;
                        if (curY < smallestY) smallestY = curY;
                        if (curY > biggestY) biggestY = curY;

                    }

                    int width = biggestX - smallestX;
                    int height = biggestY - smallestY;

                    Bitmap licensePlate = Utils.bitmapCopy(original, new Rectangle(smallestX, smallestY, width, height));

                    Threshold th = new Threshold();
                    int thres = th.getOtsuThreshold(licensePlate);
                    licensePlate = Utils.toBinary(licensePlate, (byte)thres);
                    candidates2[i] = licensePlate;
                }
            }
            return candidates2;
        }

        public Bitmap drawNewPicture(Point[][] candidates)
        {
            UnsafeBitmap newPic = new UnsafeBitmap(pictureWidth, pictureHeight);
            newPic.LockBitmap();

            for (int i = 0; i < candidates.Length; i++)
            {
                if (candidates[i] != null)
                {

                    int smallestX = pictureWidth;
                    int smallestY = pictureHeight;
                    int biggestX = 0;
                    int biggestY = 0;

                    for (int j = 0; j < 4; j++)
                    {
                        int curX = candidates[i][j].X;
                        int curY = candidates[i][j].Y;
                        if (curX < smallestX) smallestX = curX;
                        if (curX > biggestX) biggestX = curX;
                        if (curY < smallestY) smallestY = curY;
                        if (curY > biggestY) biggestY = curY;

                    }

                    for (int x = smallestX; x < biggestX; x++)
                    {
                        for (int y = smallestY; y < biggestY; y++)
                        {
                            double hue = (360 / candidates.Length) * labeledImage[getIndex(x, y)];
                            PixelData pixdata = Utils.hsvToRgb(hue, 1.0, 1.0);
                            newPic.SetPixel(x, y, pixdata);
                        }

                    }
                }
            }

            newPic.UnlockBitmap();

            return newPic.Bitmap;
        }

        public Bitmap drawNewPicture(int[][] candidates, int candidate)
        {
            UnsafeBitmap newPic = new UnsafeBitmap(pictureWidth, pictureHeight);
            newPic.LockBitmap();
            int i = candidate;

            if (candidates[i] != null)
            {
                int width = candidates[i][candidates[i].Length - 2];
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < candidates[i][candidates[i].Length - 1]; y++)
                    {
                        if (candidates[i][x + (y * width)] != 0)
                        {
                            PixelData pixdata = new PixelData();
                            pixdata.red = 255;
                            pixdata.green = 255;
                            pixdata.blue = 255;
                            newPic.SetPixel(x, y, pixdata);
                        }
                    }

                }
            }

            newPic.UnlockBitmap();

            return newPic.Bitmap;
        }

        public Bitmap drawNewPictureAllBlobs()
        {
            UnsafeBitmap newPic = new UnsafeBitmap(pictureWidth, pictureHeight);
            newPic.LockBitmap();

            for (int y = 0; y < pictureHeight; y++)
            {
                for (int x = 0; x < pictureWidth; x++)
                {
                    int label = labeledImage[getIndex(x, y)];
                    if (label != 0)
                    {
                        double hue = 360 / labeledImage[getIndex(x, y)];
                        PixelData pixdata = Utils.hsvToRgb(hue, 1.0, 1.0);
                        newPic.SetPixel(x, y, pixdata);
                    }
                }
            }

            newPic.UnlockBitmap();

            return newPic.Bitmap;
        }

        public Point[][] findCandidates(ref int[] blobCount)
        {
            Point[] begin = new Point[blobCount.Length];
            Point[] end = new Point[blobCount.Length];

            int y = 0;

            for (int i = 0; i < labeledImage.Length; i++)
            {
                int label = labeledImage[i];
                if (label != 0)
                {
                    y = i / pictureWidth;
                    if (begin[label].IsEmpty)
                    {
                        begin[label] = new Point(i - (y * pictureWidth), y);
                    }
                    end[label] = new Point(i - (y * pictureWidth), y);
                }
            }

            Point[][] cornerPoints = new Point[blobCount.Length][];

            for (int i = 1; i < blobCount.Length; i++)
            {
                cornerPoints[i] = findBlobCornersNew(i, begin[i].X, begin[i].Y, end[i].X, end[i].Y);
            }

            Point[][] candidates = new Point[10][];
            int index = 0;
            for (int i = 1; i < blobCount.Length; i++)
            {
                Point[] points = cornerPoints[i];
                double widthTop = Math.Sqrt(((points[0].X - points[1].X) * (points[0].X - points[1].X)) + (points[0].Y - points[1].Y) * (points[0].Y - points[1].Y));
                double widthBottom = Math.Sqrt(((points[2].X - points[3].X) * (points[2].X - points[3].X)) + (points[2].Y - points[3].Y) * (points[2].Y - points[3].Y));
                double heightLeft = Math.Sqrt(((points[0].X - points[2].X) * (points[0].X - points[2].X)) + (points[0].Y - points[2].Y) * (points[0].Y - points[2].Y));
                double heightRight = Math.Sqrt(((points[1].X - points[3].X) * (points[1].X - points[3].X)) + (points[1].Y - points[3].Y) * (points[1].Y - points[3].Y));

                double ratio1 = widthTop / heightRight;
                double ratio2 = widthBottom / heightLeft;
                double ratio3 = widthTop / heightLeft;
                double ratio4 = widthBottom / heightRight;

                if (ratio1 < 1)
                {
                    ratio1 = 1 / ratio1;
                }
                if (ratio2 < 1)
                {
                    ratio2 = 1 / ratio2;
                }
                if (ratio3 < 1)
                {
                    ratio3 = 1 / ratio3;
                }
                if (ratio4 < 1)
                {
                    ratio4 = 1 / ratio4;
                }

                ratio1 = Math.Round(ratio1);
                ratio2 = Math.Round(ratio2);
                ratio3 = Math.Round(ratio3);
                ratio4 = Math.Round(ratio4);

                if ((ratio1 > 3 && ratio1 < 6) || (ratio2 > 3 && ratio2 < 6) || (ratio3 > 3 && ratio3 < 6) || (ratio4 > 3 && ratio4 < 6))
                {
                    candidates[index] = points;
                    index++;
                }

                /*Console.WriteLine("Blob: " + i);
                Console.WriteLine("Point 1: " + points[0].X + "," + points[0].Y);
                Console.WriteLine("Point 2: " + points[1].X + "," + points[1].Y);
                Console.WriteLine("Point 3: " + points[2].X + "," + points[2].Y);
                Console.WriteLine("Point 4: " + points[3].X + "," + points[3].Y);
                Console.WriteLine("Ratio 1: " + ratio1);
                Console.WriteLine("Ratio 2: " + ratio2);
                Console.WriteLine("Ratio 3: " + ratio3);
                Console.WriteLine("Ratio 4: " + ratio4);
                Console.WriteLine();*/

            }
            return candidates;
        }

        //Function to find the corners of a blob
        private Point[] findBlobCornersNew(int blob, int topX, int topY, int bottomX, int bottomY)
        {
            bool continuing = true;
            int x = topX, y = topY;

            Point[] points = new Point[4];
            points[0] = new Point(topX, topY);
            points[3] = new Point(bottomX, bottomY);
            if (topX < bottomX)
            {
                while (continuing)
                {
                    if (x < pictureWidth && labeledImage[getIndex(x + 1, y)] == blob)
                    {
                        x++;
                    }
                    else if (x < pictureWidth && y > 0 && labeledImage[getIndex(x + 1, y - 1)] == blob)
                    {
                        x++;
                        y--;
                    }
                    else if (x < pictureWidth && y < pictureHeight && labeledImage[getIndex(x + 1, y + 1)] == blob)
                    {
                        x++;
                        y++;
                    }
                    else
                    {
                        continuing = false;
                    }
                }

                points[1] = new Point(x, y);
                x = bottomX; y = bottomY;
                continuing = true;

                while (continuing)
                {
                    if (x > 0 && labeledImage[getIndex(x - 1, y)] == blob)
                    {
                        x--;
                    }
                    else if (x > 0 && y < pictureHeight && labeledImage[getIndex(x - 1, y - 1)] == blob)
                    {
                        x--;
                        y--;
                    }
                    else if (x > 0 && y > 0 && labeledImage[getIndex(x - 1, y + 1)] == blob)
                    {
                        x--;
                        y++;
                    }
                    else
                    {
                        continuing = false;
                    }
                }

                points[2] = new Point(x, y);
            }
            else
            {
                while (continuing)
                {
                    if (x > 0 && labeledImage[getIndex(x - 1, y)] == blob)
                    {
                        x--;
                    }
                    else if (x > 0 && y < pictureHeight && labeledImage[getIndex(x - 1, y + 1)] == blob)
                    {
                        x--;
                        y++;
                    }
                    else if (x > 0 && y > 0 && labeledImage[getIndex(x - 1, y - 1)] == blob)
                    {
                        x--;
                        y--;
                    }
                    else
                    {
                        continuing = false;
                    }
                }

                points[1] = new Point(x, y);
                x = bottomX; y = bottomY;
                continuing = true;

                while (continuing)
                {
                    if (x < pictureWidth && labeledImage[getIndex(x + 1, y)] == blob)
                    {
                        x++;
                    }
                    else if (x < pictureWidth && y > 0 && labeledImage[getIndex(x + 1, y - 1)] == blob)
                    {
                        x++;
                        y--;
                    }
                    else if (x < pictureWidth && y < pictureHeight && labeledImage[getIndex(x + 1, y + 1)] == blob)
                    {
                        x++;
                        y++;
                    }
                    else
                    {
                        continuing = false;
                    }
                }

                points[2] = new Point(x, y);
            }


            return points;
        }

        public void removeSmallBlobs(int minSize, ref int[] blobLabels, ref int[] blobCounts)
        {
            blobCounts = new int[blobCount];

            for (int i = 0; i < blobLabels.Length; i++)
            {
                if (blobLabels[i] != 0) blobCounts[blobLabels[i]]++;
            }

            int count = 0;

            for (int i = 0; i < blobCounts.Length; i++)
            {
                if (blobCounts[i] < minSize)
                {
                    blobCounts[i] = 0;
                }

                if (blobCounts[i] > 0)
                {
                    count++;
                }
            }

            int[] filteredBlobs = new int[count + 1];
            int index = 1;
            for (int i = 0; i < blobCounts.Length; i++)
            {
                if (blobCounts[i] != 0)
                {
                    filteredBlobs[index] = i;
                    blobCounts[i] = index;
                    index++;
                }
            }

            for (int i = 0; i < blobLabels.Length; i++)
            {
                if (blobLabels[i] != 0 && blobCounts[blobLabels[i]] != 0)
                {
                    blobLabels[i] = blobCounts[blobLabels[i]];
                }
                else
                {
                    blobLabels[i] = 0;
                }
            }

            blobCounts = filteredBlobs;
        }

        public int getIndex(int x, int y)
        {
            return x + (y * pictureWidth);
        }
    }
}